<?php

namespace Plugin\Device\Service;

use App\Exception\BusinessException;
use App\Http\Common\ResultCode;
use Common\Common;
use Common\HttpClient;
use Exception;
use Hyperf\Collection\Arr;
use Plugin\Device\Repository\RdDeviceBroadcastRepository;
use Plugin\Device\Repository\RdDeviceBroadcastServerRepository;
use Plugin\File\Service\RdFileVoiceService;
use Plugin\File\Service\VoiceService;

class IpBroadcastService
{
    public function __construct(
        protected readonly RdDeviceBroadcastRepository       $rdDeviceBroadcastRepository,
        protected readonly RdFileVoiceService                $rdFileVoiceService,
        protected readonly VoiceService                      $voiceService,
        protected readonly RdDeviceBroadcastServerRepository $rdDeviceBroadcastServerRepository,
    )
    {
    }

    /**
     * ⽂本 TTS 与媒体 URL 播放接⼝
     * @param array $param 参数数组
     */
    public function speech(array $param)
    {
        $url = $param['url'] . '/v1/speech';
        return $this->request($url, 'POST', [
            'body' => [
                'device_ids' => $param['ids'],//设备编码数组
                'url' => $param['file'],//播放音频网络地址
            ]
        ], [
            'username' => $param['username'],
            'password' => $param['password'],
            'id' => $param['id'],
            'url' => $param['url'],
        ]);
    }

    /**
     * 根据传入音频的id顺序，将多个音频合并为一个音频
     * @param array $audioIds 音频ID数组
     * @param int $volume 音频音量（0-100）
     * @return string 合并后音频的URL
     * @throws Exception
     */
    public function mergeVoice(array $audioIds, int $volume = 100): array
    {
        if (empty($audioIds)) {
            throw new Exception('音频ID数组不能为空');
        }

        // 获取所有音频文件信息
        $audioFiles = [];
        foreach ($audioIds as $audioId) {
            $voice = $this->rdFileVoiceService->findById($audioId);
            if (!empty($voice->file)) {
                $filePath = $this->voiceService::getVoiceBasePath() . $voice->file;
                // 检查文件是否存在
                if (!file_exists($filePath)) {
                    throw new Exception("音频文件不存在: " . $filePath);
                }

                $audioFiles[] = [
                    'id' => $voice->id,
                    'path' => $filePath,
                    'text' => $voice->text,
                ];
            }
        }

        if (empty($audioFiles)) {
            throw new Exception('没有找到有效的音频文件');
        }

        // 如果只有一个音频文件，直接返回该文件URL
        if (count($audioFiles) == 1) {
            return $this->rdFileVoiceService->findById($audioIds[0])->toArray();
        }

        // 生成合并后的文件名，考虑音频的顺序
        $text = implode("，", array_column($audioFiles, 'text'));
        $mergedFileName = 'merged_' . md5($text) . '.wav';
        $mergedFilePath = $this->voiceService::getVoiceBasePath() . 'voice/merged/' . $mergedFileName;
        // 确保目录存在
        $mergedDir = dirname($mergedFilePath);
        if (!is_dir($mergedDir)) {
            mkdir($mergedDir, 0755, true);
        }

        // 检查是否已经合并过
        if (file_exists($mergedFilePath)) {

            if (!$voice = $this->rdFileVoiceService->getList(['text' => $text])->first()) {
                $voice = $this->rdFileVoiceService->create([
                    'file' => 'voice/merged/' . $mergedFileName,
                    'text' => $text,
                    'type' => 4,
                ]);
            }
            // 返回已存在的合并文件URL
            return $voice->toArray();
        }

        // 使用FFmpeg合并音频文件 - 使用更可靠的方法
        // 创建临时文件列表用于concat demuxer
        $tempListFile = tempnam(sys_get_temp_dir(), 'ffmpeg_concat_list_');
        $fileListContent = '';
        foreach ($audioFiles as $file) {
            $fileListContent .= "file '" . str_replace("'", "'\\''", $file['path']) . "'\n";
        }
        file_put_contents($tempListFile, $fileListContent);

        // 构建FFmpeg命令，使用concat demuxer方式
        $cmd = sprintf(
            'ffmpeg -f concat -safe 0 -i %s -c copy -y %s 2>&1',
            escapeshellarg($tempListFile),
            escapeshellarg($mergedFilePath)
        );

        // 如果需要调整音量，则使用filter方式
        if ($volume != 100) {
            $cmd = sprintf(
                'ffmpeg -f concat -safe 0 -i %s -af "volume=%f" -y %s 2>&1',
                escapeshellarg($tempListFile),
                $volume / 100,
                escapeshellarg($mergedFilePath)
            );
        }

        // 执行命令
        $output = [];
        $returnCode = 0;
        exec($cmd, $output, $returnCode);

        // 清理临时文件
        unlink($tempListFile);

        if ($returnCode !== 0) {
            throw new Exception('音频合并失败: ' . implode("\n", $output));
        }
        return $this->rdFileVoiceService->create([
            'file' => 'voice/merged/' . $mergedFileName,
            'text' => $text,
            'type' => 4,
        ])->toArray();
    }

    public function syncDeviceStatus(array $server)
    {
        $server['auth'] = Common::aesDecrypt($server['auth'], env('AES_KEY'));
        if (empty($server['auth']) && (empty($server['username']))) {
            return;
        }
        $server['password'] = Common::aesDecrypt($server['password'], env('AES_KEY'));
        $exists = $this->rdDeviceBroadcastRepository->getQuery()->where('server_id', $server['id'])
            ->pluck('code')->toArray();
        $server['url'] = $server['link'];
        $devices = $this->device($server);
        foreach ($devices as $device) {
            if (!in_array($device['code'], $exists)) {
                continue;
            }
            $this->rdDeviceBroadcastRepository->getQuery()
                ->where('code', $device['code'])
                ->where('status', '!=', $device['online'])
                ->update([
                    'status' => $device['online'],
                    'ip' => $device['client_ip'],
                    'device_id' => $device['id'],
                ]);
        }
    }

    /**
     * 设备控制接口
     * @param array $param 参数数组
     */
    public function device(array $param)
    {
        $url = $param['url'] . '/v1/device';
        $res = $this->request($url, 'GET', ['query' => $param['param'] ?? []], $param);
        if (isset($res['content']) && Arr::get($res['content'], 'code') == 200) {
            return $res['content']['data'];
        }
        return [];
    }

    private function getHeader($param)
    {
        if (
            isset($param['url']) && isset($param['id']) &&
            (
                isset($param['auth']) ||
                (isset($param['username']) && isset($param['password']))
            )
        ) {
            if ($token = Common::redis()->get('broadcast:server:' . $param['id'])) {
                return ['access_token' => $token];
            } else {
                return ['access_token' => $this->login($param)];
            }
        };
        throw new BusinessException(ResultCode::UNPROCESSABLE_ENTITY, '缺少认证参数');
    }

    public function login(array $param)
    {
        $url = $param['url'] . '/v1/login';
        $redis = Common::redis();
        $data = $this->request($url, 'POST', [
            'body' => [
                'username' => $param['username'],
                'password' => $param['password'],
            ],
        ], [], false);
        if (!isset($data['content']) || Arr::get($data['content'], 'code') != 200) {
            Common::log(__CLASS__, 'ip广播登录失败，param ' . json_encode($param, JSON_UNESCAPED_UNICODE) . ' data ' . json_encode($data, JSON_UNESCAPED_UNICODE));
            throw new BusinessException(ResultCode::FORBIDDEN, '广播登录失败');
        }
        $redis->setex('broadcast:server:' . $param['id'], 3570, $data['content']['value']['token']);
        return $data['content']['value']['token'];
    }

    private function request($uri, $method, $param, $authParam, $auth = true)
    {
        $http = new HttpClient([
            'timeout' => 5,
        ]);

        // 处理认证header
        if ($auth) {
            $headers = $this->getHeader($authParam);
        } else {
            $headers = [];
        }

        // 根据方法类型发送请求
        return match (strtoupper($method)) {
            'GET' => $http->get($uri, array_merge($param, ['headers' => $headers])),
            'POST' => $http->postJson($uri, $param['body'] ?? [], [
                'headers' => $headers
            ]),
            'PUT' => $http->putJson($uri, $param['body'] ?? [], [
                'headers' => $headers
            ]),
            'DELETE' => $http->delete($uri, $param['body'] ?? [], 'application/json', [
                'headers' => $headers
            ]),
            default => throw new BusinessException(ResultCode::UNPROCESSABLE_ENTITY, '不支持的请求方法'),
        };
    }
}